#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include <psp2/system_param.h>
#include <psp2/io/fcntl.h>
#include <psp2/touch.h>

#include <vita2d.h>

#include "browser.h"
#include "ui.h"
#include "main.h"
#include "utils.h"
#include "file.h"
#include "config.h"
#include "lang.h"
#include "listview.h"
#include "avplayer.h"

#define VIEW_MARGIN 2.0f

#define PATH_PADDING_T 6.0f
#define PATH_PADDING_L 10.0f
#define PATH_TEXT_COLOR SPRING_GREEN

#define NAME_PADDING_T 10.0f
#define NAME_PADDING_L 10.0f
#define NAME_TEXT_LINE_SPACE 9.0f

#define FOLDER_TEXT_COLOR WHITE
#define FILE_TEXT_COLOR SPRING_GREEN
#define NAME_TEXT_COLOR_FOCUS_BG COLOR_ALPHA(AZURE, 0x8F)

#define PREVIEW_PADDING 8.0f
#define PREVIEW_LOAD_WAIT_COUNT 6.0f

InstructionsEntry browser_instructions_entries[] = {
    {{STR_BUTTON_CANCEL, NULL}, STR_PARENT_DIRECTORY},
    {{STR_BUTTON_ENTER, NULL}, STR_OPEN},
    {{STR_BUTTON_START, NULL}, STR_CHANGE_DIRECTORY},
    {{0}, NULL},
};

static void setFocusByName(const char *name);
static int refreshFileList();
static void handleFolder(FileListEntry *filepEntry);
static void handleFile(FileListEntry *filepEntry);

static Image *format_folder_tex = NULL, *format_media_tex = NULL;

// File lists
static FileList fileList;
static ListView *fileListView = NULL;

// Position
static int topPosList[MAX_DIR_LEVELS];
static int focusPosList[MAX_DIR_LEVELS];
static int dir_level = 0;

static float listview_sx, listview_sy;
static float listview_width, listview_height;

void initBrowserDrawInfo()
{
    listview_width = MAIN_FREE_DRAW_WIDTH;
    listview_height = MAIN_FREE_DRAW_HEIGHT;
    listview_sx = MAIN_FREE_DRAW_SX;
    listview_sy = MAIN_FREE_DRAW_SY;
}

FileList browserGetFilelist()
{
    return fileList;
}

int currentPathIsFile()
{
    int focus_pos = ListViewGetFocusPos(fileListView);
    FileListEntry *entry = fileListGetEntryByNumber(&fileList, focus_pos);
    if (!entry)
        return 0;
    if (!entry->is_folder)
        return 1;

    return 0;
}

int makeCurFileName(char *name)
{
    int focus_pos = ListViewGetFocusPos(fileListView);
    FileListEntry *entry = fileListGetEntryByNumber(&fileList, focus_pos);
    if (!entry)
        goto failed;

    snprintf(name, MAX_NAME_LENGTH, entry->name);

    return 0;

failed:
    name[0] = '\0';
    return -1;
}

int makeCurFilePath(char *path)
{
    int focus_pos = ListViewGetFocusPos(fileListView);
    FileListEntry *entry = fileListGetEntryByNumber(&fileList, focus_pos);
    if (!entry)
        goto failed;

    snprintf(path, MAX_PATH_LENGTH, "%s%s", fileList.path, entry->name);

    return 0;

failed:
    path[0] = '\0';
    return -1;
}

static void dirChild()
{
    int top_pos = ListViewGetTopPos(fileListView);
    int focus_pos = ListViewGetFocusPos(fileListView);

    if (dir_level < MAX_DIR_LEVELS - 1)
    {
        topPosList[dir_level] = top_pos;
        focusPosList[dir_level] = focus_pos;
        dir_level++;
        topPosList[dir_level] = 0;
        focusPosList[dir_level] = 0;
    }
}

static void dirParent()
{
    removeEndSlash(fileList.path);

    char *p;
    p = strrchr(fileList.path, '/');
    if (p)
    {
        p[1] = '\0';
        dir_level--;
        goto DIR_UP_RETURN;
    }

    p = strrchr(fileList.path, ':');
    if (p)
    {
        if (strlen(fileList.path) - ((p + 1) - fileList.path) > 0)
        {
            p[1] = '\0';
            dir_level--;
            goto DIR_UP_RETURN;
        }
    }

    strcpy(fileList.path, HOME_PATH);
    dir_level = 0;

DIR_UP_RETURN:
    if (dir_level < 0)
        dir_level = 0;
}

static void setFocusByName(const char *name)
{
    int name_pos = fileListGetNumberByName(&fileList, name);
    if (name_pos < 0 || name_pos >= fileList.length)
        return;

    int top_pos = name_pos - ListViewGetDrawLines(fileListView) / 2;
    ListViewRefreshPosByFocusPos(fileListView, top_pos, name_pos, SCROLL_NONE);
    topPosList[dir_level] = ListViewGetTopPos(fileListView);
    focusPosList[dir_level] = ListViewGetFocusPos(fileListView);
}

static int changeToDirectory(char *lastdir)
{
    if (!checkFolderExist(lastdir))
        return -1;

    dir_level = 0;
    strcpy(fileList.path, HOME_PATH);
    int i;
    for (i = 0; i < strlen(lastdir) + 1; i++)
    {
        if (lastdir[i] == ':' || lastdir[i] == '/')
        {
            char ch = lastdir[i + 1];
            lastdir[i + 1] = '\0';

            char ch2 = lastdir[i];
            lastdir[i] = '\0';

            char *p = strrchr(lastdir, '/');
            if (!p)
                p = strrchr(lastdir, ':');
            if (!p)
                p = lastdir - 1;

            lastdir[i] = ch2;

            refreshFileList();
            setFocusByName(p + 1);

            strcpy(fileList.path, lastdir);

            lastdir[i + 1] = ch;

            dirChild();
        }
    }
    refreshFileList();

    return 0;
}

int changeToDirectoryFromPath(const char *path)
{
    int ret;

    char lastdir[MAX_PATH_LENGTH];
    ret = makeBaseDirectory(lastdir, path, MAX_PATH_LENGTH);
    if (ret < 0)
        return ret;

    ret = changeToDirectory(lastdir);
    if (ret < 0)
        return ret;

    char name[MAX_NAME_LENGTH];
    ret = makeFilename(name, path, MAX_NAME_LENGTH);
    if (ret < 0)
        return ret;

    setFocusByName(name);

    return 0;
}

int changeToDirectoryFromFile(const char *path)
{
    char lastfile[MAX_PATH_LENGTH];
    if (ReadFile(path, lastfile, sizeof(lastfile)) <= 0)
        return -1;

    return changeToDirectoryFromPath(lastfile);
}

static int refreshFileList()
{
    int ret = 0, res = 0;
    int sort_mode = SORT_BY_NAME;

    do
    {
        fileListEmpty(&fileList);

        res = fileListGetEntries(&fileList, fileList.path, sort_mode);

        if (res < 0)
        {
            ret = res;
            dirParent();
        }
    } while (res < 0);

    ListViewRefreshList(fileListView, &fileList);
    ListViewRefreshPosByFocusPos(fileListView, topPosList[dir_level], focusPosList[dir_level], SCROLL_NONE);

    return ret;
}

static void handleFile(FileListEntry *filepEntry)
{
    int ret = AvplayerPlayWithFileList(&fileList, filepEntry->name, PLAY_MODE_SEQUENTIAL);
    if (ret >= 0)
        changeToDirectoryFromFile(LASTFILE_PATH);
}

static void handleFolder(FileListEntry *filepEntry)
{
    if (dir_level == 0)
    {
        strcpy(fileList.path, filepEntry->name);
    }
    else
    {
        if (dir_level > 1)
            addEndSlash(fileList.path);
        strcat(fileList.path, filepEntry->name);
    }
    dirChild();
    refreshFileList();
}

static void onItemClick(ListView *listView)
{
    FileList *list = (FileList *)listView->list;
    int focus_pos = ListViewGetFocusPos(listView);

    if (list->selected_num > 0)
    {
        FileListEntry *entry = fileListGetEntryByNumber(list, focus_pos);
        if (entry)
        {
            if (entry->is_selected)
            {
                entry->is_selected = 0;
                list->selected_num--;
            }
            else
            {
                entry->is_selected = 1;
                list->selected_num++;
            }
        }
    }
    else
    {
        FileListEntry *filepEntry = fileListGetEntryByNumber(list, focus_pos);
        if (filepEntry->is_folder)
            handleFolder(filepEntry);
        else
            handleFile(filepEntry);
    }
}
#if 0
static void onItemLongClick(ListView *listView)
{
    FileList *list = (FileList *)listView->list;
    int focus_pos = ListViewGetFocusPos(listView);

    if (list->selected_num == 0 && dir_level > 0)
    {
        FileListEntry *entry = fileListGetEntryByNumber(list, focus_pos);
        if (entry)
        {
            entry->is_selected = 1;
            list->selected_num++;
        }
    }
}
#endif
static void backEvent()
{
    if (fileList.selected_num > 0)
    {
        fileListUnselectAll(&fileList);
    }
    else
    {
        // Not at 'home'
        if (dir_level > 0)
        {
            dirParent();
            refreshFileList();
        }
    }
}

static int getListLenghCallback(void *list)
{
    FileList *pList = (FileList *)list;
    if (pList)
        return pList->length;

    return 0;
}

static void *getEntryByNumberCallback(void *list, int index)
{
    FileList *pList = (FileList *)list;
    if (pList)
        return fileListGetEntryByNumber(pList, index);

    return NULL;
}

static void *getNextEntryCallback(void *list, void *entry, int index)
{
    FileListEntry *pEntry = (FileListEntry *)entry;
    if (pEntry)
        return pEntry->next;

    return NULL;
}

static int getImageViewDataCallback(ImageView *view, void *list, void *entry, int index)
{
    FileListEntry *pEntry = (FileListEntry *)entry;
    if (pEntry)
    {
        if (pEntry->is_folder)
            view->datas.image = format_folder_tex;
        else
            view->datas.image = format_media_tex;
    }

    return 0;
}

static int getNameViewDataCallback(TextView *view, void *list, void *entry, int index)
{
    FileListEntry *pEntry = (FileListEntry *)entry;
    if (pEntry)
        view->datas.text = pEntry->name;

    return 0;
}

static int getInfoViewDataCallback(TextView *view, void *list, void *entry, int index)
{
    FileListEntry *pEntry = (FileListEntry *)entry;
    if (pEntry)
        view->datas.text = pEntry->info;

    return 0;
}

static int getCheckBoxViewDataCallback(CheckBoxView *view, void *list, void *entry, int index)
{
    FileList *pList = (FileList *)list;
    if (pList)
        view->params.visibility = (pList->selected_num > 0);

    FileListEntry *pEntry = (FileListEntry *)entry;
    if (pEntry)
        view->datas.is_checked = pEntry->is_selected;

    return 0;
}

static void browserLoadImages()
{
    if (!format_folder_tex)
        format_folder_tex = vita2d_load_PNG_file(FORMAT_FOLDER_PATH);
    if (!format_media_tex)
        format_media_tex = vita2d_load_PNG_file(FORMAT_MEDIA_PATH);
}

static void browserUnoadImages()
{
    vita2d_wait_rendering_done();
    if (format_folder_tex)
        vita2d_free_texture(format_folder_tex);
    format_folder_tex = NULL;
    if (format_media_tex)
        vita2d_free_texture(format_media_tex);
    format_media_tex = NULL;
}

static void browserCreatListView()
{
    if (!fileListView)
    {
        memset(topPosList, 0, sizeof(topPosList));
        memset(focusPosList, 0, sizeof(focusPosList));
        memset(&fileList, 0, sizeof(FileList));

        ListViewParams params;
        memset(&params, 0, sizeof(ListViewParams));
        params.layout_sx = listview_sx;
        params.layout_sy = listview_sy;
        params.layout_width = listview_width;
        params.layout_height = listview_height;
        params.padding_left = 0;
        params.padding_right = 0;
        params.padding_top = 0;
        params.padding_bottom = 0;
        params.listview_bg_color = DEFALUT_BG_COLOR;
        params.itemview_bg_color = 0;
        params.enable_info = 1;
        params.enable_image = 1;
        params.enable_checkBox = 0;
        params.disable_divider = 0;

        ListViewCallbacks callbacks;
        memset(&callbacks, 0, sizeof(ListViewCallbacks));
        callbacks.getListLengh = getListLenghCallback;
        callbacks.getEntryByNumber = getEntryByNumberCallback;
        callbacks.getNextEntry = getNextEntryCallback;
        callbacks.getImageViewData = getImageViewDataCallback;
        callbacks.getNameViewData = getNameViewDataCallback;
        callbacks.getInfoViewData = getInfoViewDataCallback;
        callbacks.getCheckBoxViewData = getCheckBoxViewDataCallback;

        fileListView = NewListView(&params, &callbacks, &fileList);
    }

    ListViewSetOnItemClikListener(fileListView, &onItemClick);
    // ListViewSetOnItemLongClikListener(fileListView, &onItemLongClick);
    ListViewSetGamepadMode(fileListView, 1);
}

int initBrowser()
{
    memset(topPosList, 0, sizeof(topPosList));
    memset(focusPosList, 0, sizeof(focusPosList));
    memset(&fileList, 0, sizeof(FileList));

    browserLoadImages();
    browserCreatListView();

    strcpy(fileList.path, HOME_PATH);
    changeToDirectoryFromFile(LASTFILE_PATH);
    refreshFileList();

    return 0;
}

int finishBrowser()
{
    browserUnoadImages();
    DestroyListView(fileListView);
    fileListView = NULL;

    return 0;
}

int drawBrowser()
{
    ListViewDraw(fileListView);

    return 0;
}

int ctrlBrowser()
{
    TouchReaport *reaport = &touch_pad.reaports[0];

    if (touch_pad.back_event_mode || touch_pad.back_event_clicked)
    {
        if (touch_pad.back_event_clicked)
            backEvent();

        return 0;
    }

    ListViewEvent(fileListView);

    int gamepad_mode = ListViewGetTouchMode(fileListView);

    // Move
    if (ctrl_pad.hold[PAD_UP] || ctrl_pad.hold2[PAD_LEFT_ANALOG_UP])
    {
        if (!gamepad_mode)
            ListViewSetGamepadMode(fileListView, 1);
        else
            ListViewMoveFocusPos(fileListView, MOVE_TYPE_PREV);
    }
    else if (ctrl_pad.hold[PAD_DOWN] || ctrl_pad.hold2[PAD_LEFT_ANALOG_DOWN])
    {
        if (!gamepad_mode)
            ListViewSetGamepadMode(fileListView, 1);
        else
            ListViewMoveFocusPos(fileListView, MOVE_TYPE_NEXT);
    }
    else if (ctrl_pad.hold[PAD_LEFT])
    {
        if (!gamepad_mode)
            ListViewSetGamepadMode(fileListView, 1);
        else
            ListViewMoveFocusPos(fileListView, MOVE_TYPE_PREV_PAGE);
    }
    else if (ctrl_pad.hold[PAD_RIGHT])
    {
        if (!gamepad_mode)
            ListViewSetGamepadMode(fileListView, 1);
        else
            ListViewMoveFocusPos(fileListView, MOVE_TYPE_NEXT_PAGE);
    }

    // Open menu
    if (ctrl_pad.released[PAD_TRIANGLE])
    {
    }

    if (ctrl_pad.released[PAD_SQUARE])
    {
        if (!gamepad_mode)
        {
            ListViewSetGamepadMode(fileListView, 1);
        }
        else
        {
            if (dir_level > 0)
            {
                int focus_pos = fileListView->states.focus_pos;
                FileListEntry *entry = fileListGetEntryByNumber(&fileList, focus_pos);
                if (entry->is_selected)
                {
                    entry->is_selected = 0;
                    fileList.selected_num--;
                }
                else
                {
                    entry->is_selected = 1;
                    fileList.selected_num++;
                }
            }
        }
    }

    if (ctrl_pad.released[PAD_CANCEL])
    {
        if (!gamepad_mode)
            ListViewSetGamepadMode(fileListView, 1);
        backEvent();
    }
    else if (ctrl_pad.released[PAD_ENTER])
    {
        if (!gamepad_mode)
            ListViewSetGamepadMode(fileListView, 1);
        else
            onItemClick(fileListView);
    }
    else if (ctrl_pad.released[PAD_START])
    {
        if (!gamepad_mode)
            ListViewSetGamepadMode(fileListView, 1);
        changeToDirectoryFromFile(LASTFILE_PATH);
    }

    return 0;
}
